package TestSystemServer;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Random;
import TestSystemUtil.*;

public class ServerThread extends Thread {
	// ===== CÁC BIẾN LIÊN QUAN ĐẾN INPUT OUTPUT  SOCKET =====//
	public Socket clntSock;
	public BufferedReader in;
	public BufferedWriter out;
	public Protocol protocol = new Protocol();
	// =======================================================//
	
	// ===== CÁC BIẾN LIÊN QUAN ĐẾN VIỆC XỬ LÝ THÔNG TIN =====//
	public ArrayList<String> infoFromClient, onlineIDs;
	public ArrayList<Integer> numCreated;
	public boolean online = false;
	public boolean inGroup = false;
	public String messageFromClient = null, messageToClient = null, sql = null, hostname;
	public String uid = null, username = null, password, fullname, course, email, phone;
	public String qid = null, level, field, question, choiceA, choiceB, choiceC, choiceD, answer, clientAnswer, questionAnswered, highscore,date;
	public int randomField, max = 100, second, score=0, GID;
	// =======================================================//	
	public ServerThread(Socket clntSock, String name) {
		super(name);
		this.clntSock = clntSock;
	}

	public void run() {
		try {
			in = new BufferedReader(new InputStreamReader(clntSock.getInputStream()));
			out = new BufferedWriter(new OutputStreamWriter(clntSock.getOutputStream()));		
			while((messageFromClient = in.readLine()) != null) {
				System.out.println("Server received: " + messageFromClient);
				infoFromClient = protocol.analyseMessageFromClient(messageFromClient);		
				try {
					switch(infoFromClient.get(0).toString()) {
						case "LOGIN":
							login();
							break;
						case "REGISTER":
							register();
							break;
						case "PLAY":
							play();
							break;
						case "LOGOUT":
							logout();
							break;
						case "EXIT":
							logout();
							break;
						case "INVITE":
							invite();
							break;
						case "ACCEPT":
							acceptInvite();
							break;
						case "DECLINE":
							declineInvite();
							break;
						case "LEAVEGROUP":
							leaveGroup();
							break;
						case "GROUPPLAY":
							groupPlay();
							break;
						case "GROUPSUBMIT":
							groupResult();
							break;
						case "GET_HIGHSCORE":
							getHighscore();
							break;
						case "INTERRUPT":
							interrupt();
							break;
						case "SINGLESUBMIT":
							setPlayScore();
							break;
						case "GET_HISTORY":
							getHistory();
							break;
						default:
					} 
				} catch (Exception ex) {
					System.err.println(ex);
				}
			}
		} catch (IOException ex) {
			Server.connectingThreads.remove(this);
		} finally {
			
		}
	}

    public void interrupt() {
    	messageToClient = "INTERRUPT" + "\n";
		Sender.sendToClient(out, messageToClient);
		System.out.print("Server sent: " + messageToClient);
    }
    public void register() throws SQLException {
    	sql = "SELECT * FROM users WHERE username = ?";
		username = infoFromClient.get(1);
		password = infoFromClient.get(2);
		fullname = infoFromClient.get(3);
		course = infoFromClient.get(4);
		email = infoFromClient.get(5);
		phone = infoFromClient.get(6);
		switch(DBUtil.checkIDExist(username, sql)) {
			case DBUtil.SUCCESS:
				sql = "INSERT INTO users VALUES (default, ?, ?, ?, ?, ?, ?)";
				DBUtil.registerNewID(username, password, fullname, course, email, phone, sql);
				sql = "INSERT INTO online_users VALUES (?, 'f')";
				DBUtil.insertIntoOnlineUsers(DBUtil.getIDFromUsername(username), sql);
				messageToClient = "REGISTER+Register Succesful\n";
				break;
			case DBUtil.ERR_IDEXISTED:
				messageToClient = "REGISTER+Username was chosen\n";
				break;
		}
		Sender.sendToClient(out, messageToClient);
		System.out.print("Server sent: " + messageToClient);
    }
    
    //================== USER LOGIN CONTROL ==================//	
    public void login() throws NumberFormatException, SQLException {
    	sql = "SELECT * FROM users WHERE username = ?";
		username = infoFromClient.get(1);
	 	password = infoFromClient.get(2);
	 	
		switch(DBUtil.checkIDValid(username, password, sql)) {
			case DBUtil.SUCCESS:
				uid = Server.rs.getString(1);
				fullname = Server.rs.getString(4);
				email = Server.rs.getString(6);
				phone = Server.rs.getString(7);
				sql = "UPDATE online_users SET online = 't' WHERE \"UID\" = ?";
				DBUtil.setOnline(uid, sql);
				System.out.println(username + " online");
				online = true;
				onlineIDs = DBUtil.getOnlineUsersID();
				messageToClient = "ONLINE";
				for(int i=0; i<onlineIDs.size(); i++) {
					messageToClient = String.format(messageToClient+"+%s", DBUtil.getUsernameFromID(Integer.parseInt(onlineIDs.get(i))));
				}
				messageToClient = messageToClient + "\n";
				Sender.sendToClient(out, messageToClient);
				System.out.print("Server sent: " + messageToClient);
				messageToClient = String.format("SUCCESS+%s+%s+%s", fullname, email, phone) + "\n";				
				break;
			case DBUtil.ERR_IDNOTEXIST:
				messageToClient = "ERR+Username doesnt exist\n";
				break;
			case DBUtil.ERR_PWDISWRONG:
				messageToClient = "ERR+Password is wrong\n";
				break;
		}
		if(Server.connectingThreads.size() > 1) {
			Server.broadcast(username, online);
		}
		Sender.sendToClient(out, messageToClient);
		System.out.print("Server sent: " + messageToClient);
    }
    //========================================================//
    
    //================= SINGLE PLAY CONTROL ==================//	
    public void play() throws SQLException {
    	sql = "SELECT * FROM questions WHERE \"QID\" = ?";
		level = infoFromClient.get(1);
		field = infoFromClient.get(2);
				       
        Random randomGenerator = new Random();   
        int randomNum;
        do {
            randomNum = randomGenerator.nextInt(max)+1;
            String id = String.format("%04d", randomNum);
            qid = level + field + id;
        } while(DBUtil.checkQIDExist(qid, sql) != DBUtil.SUCCESS);
        question = Server.rs.getString(4);
        choiceA = Server.rs.getString(5);
        choiceB = Server.rs.getString(6);
        choiceC = Server.rs.getString(7);
        choiceD = Server.rs.getString(8);
		answer = Server.rs.getString(9);
		messageToClient = String.format("SUCCESS+%s+%s+%s+%s+%s+%s+%s", qid, question, choiceA, choiceB, choiceC, choiceD, answer) + "\n";;
		Sender.sendToClient(out, messageToClient);
		System.out.print("Server sent: " + messageToClient);
    }
    //========================================================//	
  
    //=================== LOG OUT CONTROL ====================//	
    public void logout() throws SQLException {
    	sql = "UPDATE online_users SET online = 'f' WHERE \"UID\" = ?";
		DBUtil.setOffline(uid, sql);
		System.out.println(username + " offline");
		online = false;
		Server.broadcast(username, online);
    }
    //========================================================//
    
    //======================== INVITE ========================//
    public void invite() throws SQLException {
    	if(!inGroup) {											//Nếu chưa ở trong group (lần đầu invite ở single player frame)
			Server.groupThreads.add(this);								//Khi nhận được tin INVITE thì sẽ add thread host vào groupThreads
			GID = DBUtil.getGroupID();
			inGroup = true;
		}
    	if(inGroup) {
			if(!username.equals(Server.groupThreads.get(0).username)) {
				messageToClient = "ERR+You're not host thread" +"\n";
				Sender.sendToClient(out, messageToClient);
				System.out.print("Server sent: " + messageToClient);
			} else if(Server.groupThreads.size() >= 4) {
				messageToClient = "ERR+Group Full" +"\n";
				Sender.sendToClient(out, messageToClient);
				System.out.print("Server sent: " + messageToClient);
			} else {
				hostname = username;
				Server.sendInviteMessage(username, infoFromClient.get(1));	//Gửi tin invite đến thread target
			}
    	}
    }
    //========================================================//
    
    //===================== ACCEPT INVITE ====================//
    public void acceptInvite() throws SQLException {
    	Server.groupThreads.add(this);									//Khi server nhận được tin accept từ thread target, add vào groupThread
		inGroup = true;
		hostname = infoFromClient.get(1);
		Server.sendAcceptMessage(hostname, username);		//Gửi tin báo thread target đã accept invite tới threat host
		sql = "INSERT INTO group_score VALUES (?, ?, ?)";
		if(Server.groupThreads.size() == 2) {
			DBUtil.insertNewGroup(hostname, sql);
			DBUtil.insertIntoExistedGroup(hostname, username, sql);
		} else {
			DBUtil.insertIntoExistedGroup(hostname, username, sql);
		}
		Server.sendGroupInfo();											//Sau khi có 1 tin accept được gửi đến thread host, 
																		//thì server 
    }
    //========================================================//
    
    //=================== DECLINE INVITE =====================//
    public void declineInvite() {
		if(Server.groupThreads.size() == 1) {
			Server.groupThreads.get(0).inGroup = false;
			Server.groupThreads.clear();								//Nếu trong group mới chỉ có host, nếu nhận đc decline message thì clear
		}
		Server.sendDeclineMessage(infoFromClient.get(1), username); 	//Gửi tin báo thread target đã decline invite tới threat host
    }
    //========================================================//
    
    //==================== LEAVE GROUP =======================//
    public void leaveGroup() throws SQLException {
    	sql = "DELETE FROM group_score WHERE \"GID\" = ? AND \"UID\" = ?";
		inGroup = false;
		DBUtil.removeMemberOfGroup(hostname, username, sql);
    	Server.groupThreads.remove(this);
    	Server.sendGroupInfo();											//Gửi lại thông tin tới mảng
    	if(Server.groupThreads.size() == 1) {
    		Server.getThread(hostname).inGroup = false;					//Gán lại về false tức là ko ở trong group này
    		DBUtil.removeMemberOfGroup(hostname, hostname, sql);
    		Server.groupThreads.clear();
    	} 
    }
    //========================================================//
    
    //======================= GROUP PLAY =====================//
    public void groupPlay() throws SQLException {
    	Server.sendStartMessage();
    	String randomField = null;										//Random trường câu hỏi: eng, bio, gio, jp
    	int randomLevel;												//Random mức độ câu hỏi: khó, vừa, dễ
    	String randomID = null;											//Random ra id của câu hỏi 0001, 0023, 0123 v.v
    	String randomQID = null;										//QID của một câu hỏi
    	sql = "SELECT * FROM questions WHERE \"QID\" = ?";
    	randomLevel = new Random().nextInt(3) + 1;						//Chọn 3 vì có 3 level
    	switch(new Random().nextInt(4)) {								//Có 4 trường câu hỏi
	    	case 0: randomField = "ENG"; break;
	    	case 1:	randomField = "JP"; break;
	    	case 2:	randomField = "GEO"; break;
	    	case 3:	randomField = "BIO"; break;
    	}
		do {
			randomID = String.format("%04d", new Random().nextInt(max) + 1);
    		randomQID = randomLevel + randomField + randomID;
    	} while(DBUtil.checkQIDExist(randomQID, sql) != DBUtil.SUCCESS);		//Vòng lặp do while random câu hỏi đến khi nào được'
		Server.level = String.valueOf(Server.rs.getInt(2));
		Server.field = Server.rs.getString(3);
		Server.question = Server.rs.getString(4);
		Server.choiceA = Server.rs.getString(5);
		Server.choiceB = Server.rs.getString(6);
		Server.choiceC = Server.rs.getString(7);
		Server.choiceD = Server.rs.getString(8);
		Server.answer = Server.rs.getString(9);
		String message = String.format("GROUPQUESTION+%s+%s+%s+%s+%s+%s+%s+%s", Server.level, Server.field, Server.question, Server.choiceA, 
				Server.choiceB, Server.choiceC, Server.choiceD, Server.answer) + "\n";
		Server.sendQuestionToGroup(message);							//Gửi câu hỏi tới Group
    }
	//========================================================//	
    
    //================= GROUP RESULT CONTROL =================//
    public void groupResult() throws SQLException {
    	Server.submitCount++;
    	score = DBUtil.getScoreInGroup(hostname, username);
    	clientAnswer = infoFromClient.get(1);
    	second = Integer.parseInt(infoFromClient.get(2));
    	if(clientAnswer.equals(Server.answer))  {
    		score = score + calScore(second);
    	}
    	else score = score + 0;
    	DBUtil.updateScoreInGroup(hostname, username, score);
    	if(Server.submitCount == Server.groupThreads.size()) Server.sendResultToGroup();
    }
    // =======================================================//
    
    //============== CALCULATE SCORE BY SECOND ===============//
    public int calScore(int second) {
    	if(second < 15) return 40;
    	else if(15<= second && second <30) return 30;
    	else if(30<= second && second <45) return 20;
    	else return 10;
    }
    // =======================================================//
    
    //================== HIGH SCORE CONTROL ==================//
    public void getHighscore() throws SQLException {
    	sql  = "select users.username, users.fullname, play.\"QuestionAnswered\", play.\"Score\" "
                + "from users, play where users.\"UID\" = play.\"UID\" order by play.\"Score\" desc limit 15";
		DBUtil.getHighScore(sql);
		messageToClient = "HIGHSCORE";
		while(Server.rs.next()) {
			username = Server.rs.getString(1);
			fullname = Server.rs.getString(2);
			questionAnswered = String.valueOf(Server.rs.getInt(3));
			highscore = String.valueOf(Server.rs.getInt(4));
			messageToClient = String.format(messageToClient + "+%s/%s/%s/%s", username, fullname, questionAnswered, highscore);
		}
		messageToClient = messageToClient + "\n";
		Sender.sendToClient(out, messageToClient);
		System.out.print("Server sent: " + messageToClient);
    }
    //=======================================================//
    
  //==================== SET SCORE CONTROL ==================//
    public void setPlayScore() throws SQLException {
    	sql = "insert into play values (?,?,?,?,?)";
    	int PTID = DBUtil.getPlayTurnID();
    	DBUtil.setScore(DBUtil.getIDFromUsername(infoFromClient.get(1)), PTID, infoFromClient.get(2), infoFromClient.get(3), sql);
    	DBUtil.setQuestionSet(PTID);
    	messageToClient = "SINGLESUBMIT" + "\n";
    	Sender.sendToClient(out, messageToClient);
		System.out.print("Server sent: " + messageToClient);
    	
    }
    //========================================================//
    
    //================== HISTORY CONTROL =====================//
    public void getHistory() throws SQLException {
    	sql  = "select users.username, play.\"DatePlayed\", play.\"QuestionAnswered\", play.\"Score\" "
                + "from users, play where play.\"UID\" = users.\"UID\""
                + " and users.\"UID\" = (select users.\"UID\" from users where users.username = ?)"
                + "order by play.\"DatePlayed\" desc";
    	DBUtil.getHistory(infoFromClient.get(1), sql);
    	messageToClient = "HISTORY";
    	while(Server.rs.next()) {
			username = Server.rs.getString(1);
			date = String.valueOf(Server.rs.getDate(2));
			questionAnswered = String.valueOf(Server.rs.getInt(3));
			highscore = String.valueOf(Server.rs.getInt(4));
			messageToClient = String.format(messageToClient + "+%s/%s/%s/%s", username, date, questionAnswered, highscore);
		}
		messageToClient = messageToClient + "\n";
		Sender.sendToClient(out, messageToClient);
		System.out.print("Server sent: " + messageToClient);
    }
  //========================================================//
}
 